package customer

import (
	"context"
	"errors"
	"fmt"
	"go.manyji.com/lib/log"
	"sieve_admin_server/app/dao/business_type"
	"sieve_admin_server/app/dao/income"
	pkgCron "sieve_admin_server/pkg/cron"
	"strings"
	"sync"
	"time"
)

var (
	inComeStaCronOnce = sync.Once{}
	inComeStaCron     *pkgCron.Cron
)

func (a *InComeStatisticsSrv) init() error {

	var (
		initCount = 1
		wp        = sync.WaitGroup{}
		logMsg    []string
	)

	wp.Add(initCount)
	go func() {
		err := a.dealInComeStatistics(time.Date(2023, 4, 1, 0, 0, 0, 0, time.Local))
		logMsg = append(logMsg, fmt.Sprintf("%+v", err))
		wp.Done()
	}()

	wp.Wait()
	return errors.New(strings.Join(logMsg, ","))
}

func (a *InComeStatisticsSrv) Cron() error {

	inComeStaCronOnce.Do(func() {
		inComeStaCron = pkgCron.New(pkgCron.WithLocation(time.Local))
		log.Info(fmt.Sprintf("%v", a.init()))
	})

	var (
		scheduleJobList = []struct {
			Spec    string
			Comment string
			pkgCron.FuncJob
		}{
			{
				// 同步搜索表
				Spec:    "30 0 * * *",
				Comment: "每天凌晨0点30分执行",
				FuncJob: a.DealInComeStatistics,
			},
		}
	)

	for _, v := range scheduleJobList {
		_, _ = inComeStaCron.AddFunc(v.Spec, v.FuncJob)
	}

	inComeStaCron.Run()
	return nil
}

func (a *InComeStatisticsSrv) DealInComeStatistics(args ...interface{}) {

	a.BusinessTypeSrv.WaitFinished()
	log.Info(fmt.Sprintf("%v", a.dealInComeStatistics(args...)))
}

func (a *InComeStatisticsSrv) dealInComeStatistics(args ...interface{}) error {

	var bizMap = make(map[uint64]*business_type.BusinessTypeConfiguration)
	respBizTy, err := a.BusinessTypeSrv.GetAll(context.Background())
	if err != nil {
		return err
	}

	if respBizTy == nil {
		log.Err(fmt.Sprintf("get business type configuration err"))
		return errors.New(fmt.Sprintf("get business type configuration err"))
	}
	for _, v := range respBizTy {
		bizC := v.(*business_type.BusinessTypeConfiguration)
		bizMap[bizC.ID] = bizC
	}

	type columnType struct {
		LeaderId             *int64  `json:"leader_id"`
		LeaderName           *string `json:"leader_name"`
		BizTyId              int64   `json:"business_type_id"`
		AccountId            int64   `json:"account_id"`
		SieveSourceName      string  `json:"sieve_source_name"`
		PointsConsumeTotal   uint64  `json:"points_consume_total"`
		PointsConsumeAccount uint64  `json:"points_consume_account"`
		PointsConsumeGift    uint64  `json:"points_consume_gift"`
		AgentPoints          uint64  `json:"agent_points"`
		Profit               uint64  `json:"profit"`
		TaskNumsTotal        int     `json:"task_nums_total"`
		TaskNumsValidTotal   int     `json:"task_nums_valid_total"`
	}

	var (
		t           = time.Now()
		condition   = " GROUP BY sieve_task.creator,business_type_id,sieve_source_name"
		startDate   = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.Local)
		endDate     = startDate.AddDate(0, 0, 1)
		leftJoinSQL = fmt.Sprintf("LEFT JOIN (%v) leader_user ON leader_user.id = sieve_income_statistics_users.account_id", fmt.Sprintf(`
			SELECT 
				id as id,
				leader_id as leader_id,
				account as leader_account
			FROM 
				user
			GROUP BY id,account
`))
	)

	if len(args) == 1 {
		startDate = args[0].(time.Time)
	}

	if len(args) == 2 {
		startDate, condition = args[0].(time.Time), fmt.Sprintf("AND sieve_task.creator = %v", args[1].(uint64))+condition
	}

	for now := startDate; !now.After(endDate); now = now.AddDate(0, 0, 1) {
		var (
			start    = time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, now.Location()).Unix()
			end      = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
			querySQL = fmt.Sprintf(`
				SELECT
					leader_user.leader_id AS leader_id,
					leader_user.leader_account AS leader_name,
					sieve_income_statistics_users.business_type_id AS business_type_id,
					sieve_income_statistics_users.account_id AS account_id,
					sieve_income_statistics_users.sieve_source_name AS sieve_source_name,
					sieve_income_statistics_users.points_consume_total,
					sieve_income_statistics_users.points_consume_account,
					sieve_income_statistics_users.points_consume_gift,
					sieve_income_statistics_users.agent_points,
					sieve_income_statistics_users.profit,
					sieve_income_statistics_users.task_nums_total,
					sieve_income_statistics_users.task_nums_valid_total
				FROM (
					SELECT
						sieve_task.creator as account_id,
						business_type_id as business_type_id,
						sieve_task_aggregation.sieve_source_name as sieve_source_name,
						SUM(points_consume_total) AS points_consume_total,
						SUM(points_consume_account) AS points_consume_account,
						SUM(points_consume_gift) AS points_consume_gift,
						SUM(agent_points) AS agent_points,
						SUM(profit) AS profit,
						SUM(task_number) AS task_nums_total,
						SUM(valid_number) AS task_nums_valid_total
					FROM sieve_task_aggregation
					INNER JOIN sieve_task ON sieve_task.id = sieve_task_aggregation.task_id AND sieve_task.deleted = 0
					WHERE sieve_task_aggregation.status = 4 AND sieve_task_aggregation.create_at >= %v AND sieve_task_aggregation.create_at <= %v
					%v
				) sieve_income_statistics_users
				%v
	`, start, end, condition, leftJoinSQL)
		)

		log.Info(fmt.Sprintf("income_statistics_cron:%v", querySQL))
		respQueries, e := a.SieveTaskAggregationRepo.QueryRaw(context.Background(), querySQL, columnType{})
		if e != nil {
			return e
		}

		for _, v := range respQueries {

			vv := v.(*columnType)
			var leaderId int64
			var leaderName string
			if vv.LeaderId != nil {
				leaderId = *vv.LeaderId
			}
			if vv.LeaderName != nil {
				leaderName = *vv.LeaderName
			}

			var desc string
			if s, ok := bizMap[uint64(vv.BizTyId)]; ok {
				desc = s.Desc
			}
			var (
				entity = income.IncomeStatistics{}
				startD = time.Unix(start, 0)
				date   = fmt.Sprintf("%04v-%02v-%02v", startD.Year(), int(startD.Month()), startD.Day())
				item   = map[string]interface{}{
					"date":                   date,
					"points_consume":         vv.PointsConsumeTotal,
					"points_consume_account": vv.PointsConsumeAccount,
					"points_consume_gift":    vv.PointsConsumeGift,
					"agent_points":           vv.AgentPoints,
					"profit":                 vv.Profit,
					"task_nums":              vv.TaskNumsTotal,
					"task_nums_valid":        vv.TaskNumsValidTotal,
					"sieve_source_name":      vv.SieveSourceName,
					"business_type_id":       vv.BizTyId,
					"business_type_desc":     desc,
					"account_id":             vv.AccountId,
					"leader_id":              leaderId,
					"leader_name":            leaderName,
					"create_at":              now.Unix(),
					"updated_at":             now.Unix(),
				}
				checkDupSQL = fmt.Sprintf(`
			SELECT 
			    * 
			FROM %v
			WHERE
			     business_type_id = %v AND leader_id = %v AND account_id = %v AND date = '%v'
`, entity.TableName(), vv.BizTyId, leaderId, vv.AccountId, date)
			)

			req, err := a.IncomeStatisticsRepo.QueryRaw(context.Background(), checkDupSQL, entity)
			if err != nil {
				log.Err("query income statistics err:%+v", err)
				continue
			}

			if req == nil {
				if err := a.IncomeStatisticsRepo.Create(context.Background(), item); err != nil {
					log.Err("create income statistics err:%+v", err)
				}
				continue
			}

			s := req[0].(*income.IncomeStatistics)
			if s.PointsConsume == vv.PointsConsumeTotal &&
				s.PointsConsumeAccount == vv.PointsConsumeAccount &&
				s.PointsConsumeGift == vv.PointsConsumeGift &&
				s.Profit == vv.Profit && s.AgentPoints == vv.AgentPoints &&
				s.BusinessTypeId == vv.BizTyId && s.SieveSourceName == vv.SieveSourceName {
				continue
			}

			var (
				queries = map[string]interface{}{
					"id": s.ID,
				}
				updateKeys = map[string]interface{}{
					"points_consume":         vv.PointsConsumeTotal,
					"points_consume_account": vv.PointsConsumeAccount,
					"points_consume_gift":    vv.PointsConsumeGift,
					"agent_points":           vv.AgentPoints,
					"profit":                 vv.Profit,
					"task_nums":              vv.TaskNumsTotal,
					"task_nums_valid":        vv.TaskNumsValidTotal,
					"sieve_source_name":      vv.SieveSourceName,
					"leader_name":            leaderName,
					"updated_at":             now.Unix(),
				}
			)

			if err := a.IncomeStatisticsRepo.Update(context.Background(), updateKeys, queries); err != nil {
				log.Err("update income statistics err:%+v", err)
			}
		}
	}

	return nil
}
